﻿using Daq.Models;
using Daq.Services;
using Daq.Utils;
using Daq.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Win32;
using ScottPlot;
using ScottPlot.Plottables;
using ScottPlot.WPF;
using System;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Telerik.Windows.Controls.FieldList;
using Telerik.Windows.Documents.Spreadsheet.Expressions.Functions;

namespace Daq.Views
{
    struct PlotData
    {
        public DateTime Time;
        public double Value;
    }

    class PlotDataBuffer
    {
        public RingBuffer<PlotData>? Buffer { get; set; }
        public DataStreamer? Streamer { get; set; }
        public IYAxis? YAxis { get; set; }
        public Color Color { get; set; }
        public string? Unit { get; set; }
        public bool IsVisible { get; set; }
    }

    class DataRecordBuffer
    {
        private readonly static int bufferSize = 10240;
        public double[] Buffer { get; set; } = new double[bufferSize];
        public string? Name { get; set; }
        public ulong count = 0;
        private uint step = 1;
        public DataRecordBuffer(string name)
        {
            Name = name;
        }
        public void Clear()
        {
            count = 0;
            MaxRenderIndex = 0;
        }
        public void Enqueue(double data)
        {
            if (count % step == 0)
            {
                //Debug.WriteLine($"cnt={count},step={step}");
                if (MaxRenderIndex < bufferSize)
                {
                    Buffer[MaxRenderIndex] = data;
                    MaxRenderIndex++;
                }
                else
                {
                    MaxRenderIndex = bufferSize / 2;
                    for (int i = 0; i < MaxRenderIndex; i++)
                    {
                        Buffer[i] = Buffer[i * 2];
                    }
                    step *= 2;
                }
            }
            
            count++;
        }
        public int MaxRenderIndex { get; set; }
    }

    

    public class RingBuffer<T> : IEnumerable<T>
    {
        private T[] _buffer;
        private int _capacity;
        private int _head;
        private int _tail;
        private int _count;
        private bool _isFull;

        public RingBuffer(int capacity)
        {
            if (capacity <= 0)
                throw new ArgumentException("Capacity must be greater than zero.");

            _capacity = capacity;
            _buffer = new T[capacity];
            _head = 0;
            _tail = 0;
            _count = 0;
            _isFull = false;
        }

        public void Enqueue(T item)
        {
            if (_isFull)
            {
                // 缓冲区已满，覆盖最老的数据
                _tail = (_tail + 1) % _capacity;
            }

            _buffer[_head] = item;
            _head = (_head + 1) % _capacity;
            _count = (_count + 1) % _capacity;
            _isFull = (_count == _capacity);
        }

        public T Dequeue()
        {
            if (_count == 0)
            {
                throw new InvalidOperationException("Buffer is empty.");
            }

            T item = _buffer[_tail];
            _tail = (_tail + 1) % _capacity;
            _count--;
            _isFull = false;
            return item;
        }

        public T Peek()
        {
            if (_count == 0)
            {
                throw new InvalidOperationException("Buffer is empty.");
            }

            return _buffer[_tail];
        }

        public int Count => _count;

        public bool IsEmpty => _count == 0;

        public bool IsFull => _isFull;

        public IEnumerator<T> GetEnumerator()
        {
            if (_count == 0)
            {
                yield break;
            }

            int start = _tail;
            int end = (_tail + _count) % _capacity;

            for (int i = start; i != end; i = (i + 1) % _capacity)
            {
                yield return _buffer[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

    /// <summary>
    /// DaqView.xaml 的交互逻辑
    /// </summary>
    public partial class DaqView : UserControl
    {
        private const int maxDatPointsCount = 20_000 * 5;
        private DataRecordBuffer dataRecordBuffer = new("");
        private ObservableCollection<DataRecordBuffer> dataRecordBuffers = [];
        private ObservableCollection<WpfPlotGL> plots = [];// [new WpfPlotGL {Margin = new Thickness(5), Height = 250, Width = 1000 },new WpfPlotGL { Height = 250, Width = 1000},new WpfPlotGL { Height = 250, Width = 1000}];
        private readonly PlotService plotService;
        public DaqView()
        {
            InitializeComponent();
            DataContext = App.Current.Services.GetService<DaqViewModel>().DeviceService;
            plotService = App.Current.Services.GetService<PlotService>();
            //InitPlot();
        }

        private DispatcherTimer? plotRefreshTimer;//

        //private Signal sinalSerie; 
        private void InitPlot()
        {
            var plot = liveDataPlot.Plot;

            liveDataPlot.UserInputProcessor.DoubleLeftClickBenchmark(false);
            plot.XLabel("时间");
            //plot.YLabel("测量值");
            //plot.Title("s");
            plot.ShowLegend();
            plot.Font.Automatic();
            plot.Axes.Left.IsVisible = false;

            plot.Axes.ContinuouslyAutoscale = true;

            liveDataPlot.Menu?.Clear();
            liveDataPlot.Menu?.Add("保存波形", (formsplot1) =>
            {
                OpenSaveImageDialog(liveDataPlot);
            });
            liveDataPlot.Menu?.Add("复制到剪切板", (formsplot1) =>
            {
                CopyImageToClipboard(liveDataPlot);
            });



            //plot.Legend.SymbolWidth = 100;
            //double[] values = Generate.RandomWalk(1_000_000);

            //sinalSerie = plot.Add.Signal(values);



            //var ch = liveDataPlot.Plot.Add.Crosshair(0, 0);
            //ch.TextColor = Colors.White;
            //ch.TextBackgroundColor = ch.HorizontalLine.Color;



            //liveDataPlot.MouseMove += (s, e) =>
            //{
            //    var point = e.GetPosition(liveDataPlot);
            //    Pixel mousePixel = new(point.X, point.Y);
            //    Coordinates mouseCoordinates = liveDataPlot.Plot.GetCoordinates(mousePixel);
            //    ch.Position = mouseCoordinates;
            //    ch.VerticalLine.Text = $"{mouseCoordinates.X:N3}";
            //    ch.HorizontalLine.Text = $"{mouseCoordinates.Y:N3}";
            //    liveDataPlot.Refresh();


            //    // determine where the mouse is and get the nearest point
            //    //var point = e.GetPosition(liveDataPlot);
            //    //Pixel mousePixel = new(point.X, point.Y);
            //    //Coordinates mouseLocation = liveDataPlot.Plot.GetCoordinates(mousePixel);
            //    //var serie = liveDataPlot.Plot.PlottableList[1] as DataStreamer;
            //    //DataPoint nearest = serie.Data.GetNearest(mouseLocation, liveDataPlot.Plot.LastRender);

            //    //// place the crosshair over the highlighted point
            //    //if (nearest.IsReal)
            //    //{
            //    //    ch.IsVisible = true;
            //    //    ch.Position = nearest.Coordinates;
            //    //    liveDataPlot.Refresh();
            //    //    //Text = $"Selected Index={nearest.Index}, X={nearest.X:0.##}, Y={nearest.Y:0.##}";
            //    //}

            //    //// hide the crosshair when no point is selected
            //    //if (!nearest.IsReal && ch.IsVisible)
            //    //{
            //    //    ch.IsVisible = false;
            //    //    liveDataPlot.Refresh();
            //    //    //Text = $"No point selected";
            //    //}

            //};

            //Task.Factory.StartNew(PlotTask, TaskCreationOptions.LongRunning);
            plotRefreshTimer = new DispatcherTimer(DispatcherPriority.Normal)
            {
                Interval = TimeSpan.FromMilliseconds(2),
            };
            plotRefreshTimer.Tick += plotRefreshTimerHandler;
            plotRefreshTimer.Start();


            //plot = dataRecordPlot.Plot;

            //plot.Benchmark.IsVisible = false;
            //plot.XLabel("时间"); 
            ////plot.YLabel("测量值");
            ////plot.Title("s");
            //plot.ShowLegend();
            //plot.Font.Automatic();
            //plot.Axes.Left.IsVisible = false;

            //plot.Axes.ContinuouslyAutoscale = true;
            //signal = plot.Add.Signal(dataRecordBuffer.Buffer);
        }
        private Signal signal;
        private Random random = new Random();
        private int count = 0;

        //private PerformanceTest performanceTest = new(100);

        private void plotRefreshTimerHandler(object? sender, EventArgs e)
        {
            bool valid = false;
            while (plotService.CommandBuffer.Reader.TryRead(out var buffer))
            {
                valid = true;
                HandlePlotMessage(ref buffer);
            }
            while (plotService.DataBuffer.Reader.TryRead(out var buffer))
            {
                valid = true;
                HandlePlotMessage(ref buffer);
            }

            if (!valid) return;

            //performanceTest.Begin();
            for (int i = 0; i < plots.Count; i++)
            {
                if (dataRecordBuffers.Count <= i) break;
                var plot = plots[i].Plot;

                if(plot != null && plot.PlottableList.Count > 0)
                {
                    if(plot.PlottableList[0] is Signal signal)
                    {
                        signal.MaxRenderIndex = dataRecordBuffers[i] == null ? 0 : dataRecordBuffers[i].MaxRenderIndex;
                    }
                }
                //plots[i].Refresh();
            }
            //performanceTest.End();

            if (count++ > 1)
            {
                //foreach(var p in plots)
                //{
                //    p.Refresh();
                //}
                liveDataPlot.Refresh();
                count = 0;
            }
            
            //for (int i = 0; i < 200; i++)
            //    dataRecordBuffer.Enqueue(random.NextDouble());
            //signal.MaxRenderIndex = dataRecordBuffer.MaxRenderIndex - 1;
            //dataRecordPlot.Refresh();
        }


        private int cnt = 0;
        private readonly Dictionary<string, PlotDataBuffer> series = [];

        private void HandlePlotMessage(ref PlotMessage msg)
        {
            var name = msg.ChannelName;
            switch (msg.Type)
            {
                case PlotMessageType.ClearAllSeries:
                    foreach (var key in series.Keys)
                    {
                        RemoveSerie(key);
                        series[key].Buffer = new(maxDatPointsCount);
                    }
                    dataRecordBuffers.Clear();
                    dataRecordBuffers = [];
                    plots.Clear();
                    break;
                case PlotMessageType.AddSerie:
                    if(series.TryGetValue(name, out var buf))
                    {
                        if(msg.IsVisible)
                        {
                            AddSerie(name);
                        }
                    }
                    else
                    {
                        var buffer = new PlotDataBuffer()
                        {
                            Buffer = new(maxDatPointsCount),
                            Color = msg.Color,
                            IsVisible = false,
                            Unit = msg.Unit,
                        };
                        series.Add(name, buffer);
                    }
                    var buffer1 = new DataRecordBuffer(name);
                    dataRecordBuffers.Add(buffer1);
                    var p1 = new WpfPlotGL
                    {
                        
                        Height = 250,
                        Width = 300,
                        Margin = new Thickness(5),
                        ToolTip = name
                    };
                    var plot = p1.Plot;

                    plot.Benchmark.IsVisible = false;
                    plot.XLabel("时间");
                    //plot.YLabel("测量值");
                    //plot.Title("s");
                    plot.ShowLegend();
                    plot.Font.Automatic();
                    plot.Axes.Left.IsVisible = false;

                    plot.Axes.ContinuouslyAutoscale = true;
                    signal = plot.Add.Signal(buffer1.Buffer);
                    plots.Add(p1);
                    //listboxPlots.ItemsSource = plots;
                    break;
                case PlotMessageType.RemoveSerie:
                    var dataRecordBuffer = dataRecordBuffers.FirstOrDefault(d => d.Name != name);
                    if (dataRecordBuffer != null)
                    {
                        dataRecordBuffers.Remove(dataRecordBuffer);
                    }
                    var p2 = plots.FirstOrDefault(s => s.Name == name);
                    if (p2 != null)
                    {
                        plots.Remove(p2);
                    }
                    RemoveSerie(name);
                    break;
            }
        }
        private void HandlePlotMessage(ref PlotDataMessage msg)
        {
            var plot = liveDataPlot.Plot;
            var name = msg.ChannelName;

            if (series.TryGetValue(name, out var buf))
            {
                if(buf.Streamer != null)
                {
                    buf.Streamer.Add(msg.Value);
                    buf.Streamer.LegendText = GetSerieDetail(name, msg.Average);
                }
                if(buf.Buffer != null)
                {
                    buf.Buffer.Enqueue(new PlotData
                    {
                        Time = msg.Time,
                        Value = msg.Value
                    });
                    while (buf.Buffer.Count > maxDatPointsCount)
                    {
                        buf.Buffer.Dequeue();
                    }
                }
            }

            var buffer = dataRecordBuffers.FirstOrDefault(d => d != null && d.Name == name);
            if(buffer != null)
            {
                buffer.Enqueue(msg.Value);
            }
        }


        private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            InitPlot();
            //listboxPlots.ItemsSource = plots;
            //for(int i = 0; i < 4; i++)
            //{
            //    Plot.CommandBuffer.Enqueue(new PlotMessage
            //    {
            //        Type = PlotMessageType.AddSerie,
            //        ChannelName = $"test{i}"
            //    });
            //}

            //Debug.WriteLine(Environment.CurrentManagedThreadId);
            //Task.Factory.StartNew(async () =>
            //{
            //    await Task.Delay(2000);
            //    Random random = new();
            //    Debug.WriteLine(Environment.CurrentManagedThreadId);
            //    //for(int i = 0; i < 100; i++)
            //    while (true)
            //    {
            //        for (int i = 0; i < 4; i++)
            //        {
            //            Plot.DataBuffer.Enqueue(new PlotDataMessage
            //            {
            //                ChannelName = $"test{i}",
            //                Value = random.NextDouble() + 10 + i * 1.2,
            //                Time = DateTime.Now
            //            });
            //        }
            //        await Task.Delay(1);
            //    }
            //}, TaskCreationOptions.LongRunning);
        }


        private static string CustomFormatter(double position)
        {
            if(position >= 1000)
            {
                return $"{position / 1000}s";
            }
            return $"{position}ms";
        }

        //private readonly int maxDatPointsCount = 20_000 * 5;
        private int dataPointsCount = 100;
        internal void ChangeTimeDivision(ChartTimeDivision division)
        {
            int time = division switch
            {
                //ChartTimeDivision._100s => 100_000,
                //ChartTimeDivision._50s => 50_000,
                ChartTimeDivision._20s => 20_000,
                ChartTimeDivision._10s => 10_000,
                ChartTimeDivision._5s => 5_000,
                ChartTimeDivision._2s => 2_000,
                ChartTimeDivision._1s => 1_000,
                ChartTimeDivision._500ms => 500,
                ChartTimeDivision._200ms => 200,
                ChartTimeDivision._100ms => 100,
                _ => 200
            };

            dataPointsCount = time * 5;
            var plot = liveDataPlot.Plot;
            var tickGenerator = new ScottPlot.TickGenerators.NumericFixedInterval(time);
            tickGenerator.LabelFormatter = CustomFormatter;
            plot.Axes.Bottom.TickGenerator = tickGenerator;
            

            foreach (var key in series.Keys)
            {
                if (!series[key].IsVisible) continue;
                RemoveSerie(key);
                AddSerie(key);
            }
        }

        private void checkBoxChartVisible_Unchecked(object sender, System.Windows.RoutedEventArgs e)
        {
            if(sender is CheckBox checkBox)
            {
                if(checkBox.DataContext is DaqChannelInfo ch)
                {
                    SetChartVisible(ch.Alias, false);
                }
            }
        }

        private void checkBoxChartVisible_Checked(object sender, System.Windows.RoutedEventArgs e)
        {
            if (sender is CheckBox checkBox)
            {
                if (checkBox.DataContext is DaqChannelInfo ch)
                {
                    SetChartVisible(ch.Alias, true);
                }
            }
        }

        private PlotDataBuffer? GetSeriesInDictionary(string name)
        {
            if(series.TryGetValue(name, out var buf))
            {
                return buf;
            }
            return null;
        }

        private void RemoveSerie(string name)
        {
            if (series.TryGetValue(name, out var buf))
            {
                buf.IsVisible = false;
                var plot = liveDataPlot.Plot;
                //plot.Remove(buf.Streamer);
                plot.PlottableList.Remove(buf.Streamer);
                plot.Axes.Remove(buf.YAxis);
            }
        }

        private string GetSerieDetail(string name, double avg)
        {
            if(series.TryGetValue(name, out var buf) && buf != null && buf.Streamer != null)
            {
                Span<Char> buffer = stackalloc char[256];
                int pos = 0;
                int charsWritten;
                name.AsSpan().CopyTo(buffer.Slice(pos));
                pos += name.Length;
                " \nMax=".CopyTo(buffer.Slice(pos));
                pos += 6;
                buf.Streamer.Data.DataMax.TryFormat(buffer.Slice(pos), out charsWritten,format: null, provider: null);
                pos += charsWritten;
                "  Min=".CopyTo(buffer.Slice(pos));
                pos += 6;
                buf.Streamer.Data.DataMin.TryFormat(buffer.Slice(pos), out charsWritten, format: null, provider: null);
                pos += charsWritten;
                " Avg=".CopyTo(buffer.Slice(pos));
                pos += 5;
                avg.TryFormat(buffer.Slice(pos), out charsWritten, format: null, provider: null);
                return new string(buffer);
                //return $"{name} \nMax={buf.Streamer.Data.DataMax}  Min={buf.Streamer.Data.DataMin} Avg={avg}";
            }
            return "";
        }

        private void AddSerie(string name)
        {
            if(series.TryGetValue(name,out var buf))
            {
                if(buf != null)
                {
                    buf.IsVisible = true;
                    var plot = liveDataPlot.Plot;
                    plot.Remove(buf.Streamer);
                    var streamer = plot.Add.DataStreamer(dataPointsCount);
                    streamer.ViewScrollLeft();
                    streamer.Color = buf.Color;

                    var axis = plot.Axes.AddLeftAxis();
                    axis.LabelText = $"{name}({buf.Unit})";
                    axis.LabelFontColor = buf.Color;
                    axis.Label.SetBestFont();
                    axis.SizeWhenNoData = 1;
                    streamer.Axes.YAxis = axis;
                    buf.YAxis = axis;

                    streamer.LegendText = name;
                    buf.Streamer = streamer;
                    buf.Streamer.AddRange(buf.Buffer.Select(buf => buf.Value).TakeLast(dataPointsCount).ToList());
                }
            }
        }

        private void SetChartVisible(string name, bool isVisible)
        {
            DeviceService? deviceService = App.Current.Services.GetService<DeviceService>();
            if (deviceService != null)
            {
                deviceService.SetChartVisibility(name, isVisible);
            }
            if (!isVisible && series.TryGetValue(name, out var buf))
            {
                RemoveSerie(name);
            }
            else if (isVisible)
            {
                AddSerie(name);
            }
        }

        public void OpenSaveImageDialog(string extName)
        {
            SaveFileDialog dialog = new()
            {
                FileName = "波形." + extName,
                Filter = extName switch
                {
                    "png" => "PNG Files (*.png)|*.png",
                    "jpg" => "JPEG Files (*.jpg, *.jpeg)|*.jpg;*.jpeg",
                    "bmp" => "BMP Files (*.bmp)|*.bmp",
                    _ => "PNG Files (*.png)|*.png"
                }
            };

            if (dialog.ShowDialog() is true)
            {
                if (string.IsNullOrEmpty(dialog.FileName))
                    return;

                ImageFormat format;

                try
                {
                    var ext = Path.GetExtension(dialog.FileName);
                    if (!ext.StartsWith("."))
                        throw new ArgumentException("extension must start with '.'");

                    format = ext.ToLowerInvariant() switch
                    {
                        ".jpg" or ".jpeg" => ImageFormat.Jpeg,
                        ".png" => ImageFormat.Png,
                        ".bmp" => ImageFormat.Bmp,
                        ".webp" => ImageFormat.Webp,
                        ".svg" => ImageFormat.Svg,
                        _ => throw new ArgumentException($"unknown image format: '{ext}'")
                    };
                }
                catch (ArgumentException)
                {
                    MessageBox.Show("不支持的图片格式", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
                try
                {
                    PixelSize lastRenderSize = liveDataPlot.Plot.RenderManager.LastRender.FigureRect.Size;
                    Task.Run(() =>
                    {
                        liveDataPlot.Plot.Save(dialog.FileName, (int)lastRenderSize.Width, (int)lastRenderSize.Height, format);
                    });
                }
                catch (Exception)
                {
                    MessageBox.Show("保存失败", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }
        }

        private void OpenSaveImageDialog(IPlotControl plotControl)
        {
            SaveFileDialog dialog = new()
            {
                FileName = "波形.png",
                Filter = "PNG Files (*.png)|*.png" +
                         "|JPEG Files (*.jpg, *.jpeg)|*.jpg;*.jpeg" +
                         "|BMP Files (*.bmp)|*.bmp" +
                         "|WebP Files (*.webp)|*.webp" +
                         "|SVG Files (*.svg)|*.svg" +
                         "|All files (*.*)|*.*"
            };

            if (dialog.ShowDialog() is true)
            {
                if (string.IsNullOrEmpty(dialog.FileName))
                    return;

                ImageFormat format;

                try
                {
                    var ext = Path.GetExtension(dialog.FileName);
                    if (!ext.StartsWith("."))
                        throw new ArgumentException("extension must start with '.'");

                    format = ext.ToLowerInvariant() switch
                    {
                        ".jpg" or ".jpeg" => ImageFormat.Jpeg,
                        ".png" => ImageFormat.Png,
                        ".bmp" => ImageFormat.Bmp,
                        ".webp" => ImageFormat.Webp,
                        ".svg" => ImageFormat.Svg,
                        _ => throw new ArgumentException($"unknown image format: '{ext}'")
                    };
                }
                catch (ArgumentException)
                {
                    MessageBox.Show("不支持的图片格式", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
                try
                {
                    PixelSize lastRenderSize = plotControl.Plot.RenderManager.LastRender.FigureRect.Size;
                    Task.Run(() =>
                    {
                        plotControl.Plot.Save(dialog.FileName, (int)lastRenderSize.Width, (int)lastRenderSize.Height, format);
                    });
                }
                catch (Exception)
                {
                    MessageBox.Show("保存失败", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }
        }

        private void CopyImageToClipboard(IPlotControl plotControl)
        {
            PixelSize lastRenderSize = plotControl.Plot.RenderManager.LastRender.FigureRect.Size;
            var bmp = plotControl.Plot.GetImage((int)lastRenderSize.Width, (int)lastRenderSize.Height);
            byte[] bmpBytes = bmp.GetImageBytes();

            using MemoryStream ms = new();
            ms.Write(bmpBytes, 0, bmpBytes.Length);
            BitmapImage bmpImage = new();
            bmpImage.BeginInit();
            bmpImage.StreamSource = ms;
            bmpImage.EndInit();
            Clipboard.SetImage(bmpImage);
        }

        public void LockYAxes(bool flag)
        {
            liveDataPlot.Plot.Axes.ContinuouslyAutoscale = flag;
        }
    }
}
